<?php

/*
 * Copyright 2014 T J Morgan.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * House Ledger - a web application with the intention of being useful to
 * semi-automatically track bills and payments between members of a shared household.
 * 
 * Author: Tim Morgan
 */

/*
 * File Description
 * 
 * Class to encapsulate an account in the ledger. Any person or business entity
 * that may reuire a unique name and balance record will be stored here.
 * 
 * The class maintains a dynamic list/stack of transactions - when added, they will effect
 * the local balance in some way... so multiple Accounts can reference locally the global
 * end-to-end Transaction information.
 */

namespace Ledger\Model;

class Account {

    private $name;
    private $balance;
    private $transactions;

    // basic constructor
    public function __construct($uname = null, $balance = 0, array $transactions = array()) {
        $this->name = $uname;
        $this->balance = $balance;
        
        // start with an fresh list,
        // then call addTransaction on each  new one given
        $this->transactions = array();
        array_walk($transactions, array($this, "addTransaction"));
    }
    
    // add a transaction to the pile,
    // process its particular effect on this account, or reject it if it's unrelated to us
    public function addTransaction(Bank\Transaction $transaction){
        // check that no transaction with this ID already exists in list
        if(in_array($transaction->id, array_column($this->transactions, "id")))
            throw new \ErrorException("The transaction ID is already associated with this account!");
        
        // process the transaction on the local object
        if($transaction->from == $this->name)
            $this->withdraw($transaction->amount);
        else if($transaction->to == $this->name)
            $this->deposit($transaction->amount);
        else
            // there is no effect on this account - so we shouldn't add it to our list
            throw new \InvalidArgumentException("Transaction doesn't involve this account!");
    }
    
    // might as well have the opposite function too...
    // TODO maybe amend this later, to do it as a refund transaction on top
    public function retractTransaction(Bank\Transaction $transaction){
        if($transaction->from == $this->name)
            $this->deposit($transaction->amount); // refunding myself
        else if($transaction->to == $this->name)
            $this->withdraw($transaction->amount); // refunding the other person - I lose :(
        else
            throw new \InvalidArgumentException("Transaction doesn't involve this account!");
    }
    
    public function deposit($amount) {
        if ($amount <= 0) {
            if ($amount == 0)
                throw new ErrorException("Cannot deposit zero; there is nothing to deposit!");
            throw new ErrorException("Cannot deposit a negative amount!");
        }

        $this->balance += $amount;
    }

    public function withdraw($amount) {
        if ($amount <= 0) {
            if ($amount == 0)
                throw new ErrorException("Cannot withdraw zero; there is nothing to withdraw!");
            throw new ErrorException("Unable to withdraw a negative amount!");
        }
        
        $this->balance -= $amount;
    }
    //...
    

    // constructor for zend-database integration; translate fields back and forth.
    public function exchangeArray($data) {
        $this->__construct(
            (!empty($data['name']))    ? $data['name']    : null,
            (!empty($data['balance'])) ? $data['balance'] : null
        );
    }

}
